﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Configuration;
using System.Transactions;

using Pg.BioMedics.SDR.Data.Strategy;
using Pg.BioMedics.SDR.Data.Configuration;

namespace Pg.BioMedics.SDR.Data
{
    /// <summary>
    /// Represents main entry point for data access logic
    /// </summary>
    public abstract class BaseDataContext : IDisposable
    {
        #region Constants

        internal const string ConnectionStringProperty = "ConnectionString";

        #endregion

        #region Fields

        private IDataMapperStrategy _dataMapperStrategy;
        private ITransactionStrategy _transactionStrategy;

        private bool _default;
        private string _connectionString;
        private bool _transactions;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new data context instance with configuration from application config file
        /// </summary>
        protected BaseDataContext()
        {
            try
            {
                LoadConfiguration();
                ChooseStrategies();
                BeginTransactions();
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Can't initialize data context", ex);
            }
        }

        /// <summary>
        /// Creates a new data context instance with configuration from application config file and disabled or enabled transactions in 
        /// this scope
        /// </summary>
        /// <param name="transactions">defines if context should interact with transactions</param>
        protected BaseDataContext(bool transactions)
        {
            try
            {               
                LoadConfiguration();

                _transactions = false;

                ChooseStrategies();
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Can't initialize data context", ex);
            }
        }

        /// <summary>
        /// Creates a new data context instance with given connection string
        /// </summary>
        /// <param name="connectionString">connection string</param>
        /// <param name="transactions">defines if MDTC transactions are enabled or diabled</param>
        protected BaseDataContext(string connectionString, bool transactions)
        {
            try
            {
                _default = false;
                _connectionString = connectionString;
                _transactions = transactions;

                ChooseStrategies();
                BeginTransactions();
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Can't initialize data context", ex);
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets data mapper strategy associated to this data context
        /// </summary>
        protected IDataMapperStrategy DataMapperStrategy
        {
            get
            {
                return _dataMapperStrategy;
            }
        }

        /// <summary>
        /// Gets transaction strategy associated to this data context
        /// </summary>
        protected ITransactionStrategy TransactionStrategy
        {
            get
            {
                return _transactionStrategy;
            }
        }

        /// <summary>
        /// Gets custom connection string for which this data context has been initialized
        /// </summary>
        public string ConnectionString
        {
            get
            {
                return _connectionString;
            }
        }

        /// <summary>
        /// Defaines if default SQLMap should be used
        /// </summary>
        public bool Default
        {
            get
            {
                return _default;
            }
        }

        /// <summary>
        /// Defines if transactions are enabled
        /// </summary>
        public bool Transactions
        {
            get
            {
                return _transactions;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Indicates that all operations managed by transaction have been completed successfully 
        /// </summary>
        public void Complete()
        {
            _transactionStrategy.Complete();
        }

        #endregion

        #region Support methods

        /// <summary>
        /// Loads configuration from custom configuration section and applies
        /// configuration entries to context internal configuration fields
        /// </summary>
        private void LoadConfiguration()
        {
            DataAccessConfigurationSection configuration = DataAccessConfigurationSection.GetConfiguration();
            if (configuration.Settings == null) // load default configuration
            {
                _default = true;
                _transactions = false;
            }
            else // load settings
            {
                _transactions = configuration.Settings.TransactionsEnabled;
                if (ConfigurationManager.ConnectionStrings[configuration.Settings.ConnectionStringName] != null)
                    _connectionString = ConfigurationManager.ConnectionStrings[configuration.Settings.ConnectionStringName].ConnectionString;
                else
                    _default = true;
            }
        }

        /// <summary>
        /// Provides strategies initialization basing on loaded configuration
        /// </summary>
        private void ChooseStrategies()
        {
            if ((_default) || (String.IsNullOrEmpty(_connectionString)))
            {
                _dataMapperStrategy = new DefaultDataMapperStrategy();
                _transactionStrategy = new NoTransactionStrategy();
                _default = true;
            }
            else
            {
                if (_transactions)
                {
                    _dataMapperStrategy = new SharedDataMapperStrategy(PrepareProperties());
                    _transactionStrategy = new CurrentTransactionStrategy();
                }
                else
                {
                    _dataMapperStrategy = new CustomDataMapperStrategy(PrepareProperties());
                    _transactionStrategy = new NoTransactionStrategy();
                }
            }
        }

        /// <summary>
        /// Combines parameters into properties collection. Properties are utilized during iBatis configuration
        /// </summary>
        /// <returns></returns>
        protected NameValueCollection PrepareProperties()
        {
            NameValueCollection properties = new NameValueCollection();
            properties.Add(ConnectionStringProperty, _connectionString);

            return properties;
        }

        private void BeginTransactions()
        {
            _transactionStrategy.Begin();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            _transactionStrategy.Close();            
        }

        #endregion
    }
}
